﻿using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Security;
using System.Security.Cryptography;
using SanlieHash.SecureHash;
using SanlieHash.GuoMi;
using SanlieHash;
using System.IO;
using Org.BouncyCastle.Crypto.Paddings;

namespace SignetKA.utils
{
    public class EncryptTool
    {
        public static byte[] StreamToByteArray(Stream stream)
        {
            // 确保流的位置在开始处
            stream.Position = 0;

            // 创建一个byte数组来存储流中的数据
            byte[] byteArray = new byte[stream.Length];

            // 读取流中的数据到byte数组
            stream.Read(byteArray, 0, (int)stream.Length);

            return byteArray;
        }
        public class SM4Encryption
        {
            private static readonly int BlockSize = 16; // SM4 block size is 16 bytes

            public static byte[] Encrypt(byte[] input, byte[] key, byte[] iv)
            {
                var cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new SM4Engine()), new Pkcs7Padding());
                var keyParam = new KeyParameter(key);
                var keyParamWithIV = new ParametersWithIV(keyParam, iv);

                cipher.Init(true, keyParamWithIV);

                var size = cipher.GetOutputSize(input.Length);
                var output = new byte[size];
                var len = cipher.ProcessBytes(input, 0, input.Length, output, 0);
                len += cipher.DoFinal(output, len);

                return output;
            }

            public static byte[] Decrypt(byte[] input, byte[] key, byte[] iv)
            {
                var cipher = new BufferedBlockCipher(new CbcBlockCipher(new SM4Engine()));
                var keyParam = new KeyParameter(key);
                var keyParamWithIV = new ParametersWithIV(keyParam, iv);

                cipher.Init(false, keyParamWithIV);

                var output = new byte[cipher.GetOutputSize(input.Length)];
                var len = cipher.ProcessBytes(input, 0, input.Length, output, 0);
                len += cipher.DoFinal(output, len);

                return output;
            }
        }
        public static byte[] CombineBytes(byte[] first, byte[] second)
        {
            if (first == null)
                throw new ArgumentNullException(nameof(first));
            if (second == null)
                throw new ArgumentNullException(nameof(second));

            byte[] combined = new byte[first.Length + second.Length];
            Buffer.BlockCopy(first, 0, combined, 0, first.Length);
            Buffer.BlockCopy(second, 0, combined, first.Length, second.Length);
            return combined;
        }
        public static bool VerityPwd(string pwd, byte[] key, byte[] key_tag)
        {
            var bufs_pwd = System.Text.UnicodeEncoding.Unicode.GetBytes(pwd);
            var sha128 = new SHA3_SHAKE128();
            var key_tag_now = sha128.Make(CombineBytes(key, bufs_pwd));
            return Helper.ToHexString(key_tag) == key_tag_now;
        }
        public static (byte[] key, byte[] iv, byte[] key_tag, byte[] iv_tag) MakeKeyIV(string pwd, byte[] key=null, byte[] iv=null)
        {
            var bufs_pwd = System.Text.UnicodeEncoding.Unicode.GetBytes(pwd);
            if (key == null && iv == null)
            {
                var bufs_key = new byte[16];
                var bufs_iv = new byte[16];
                var rad = new SecureRandom();
                rad.NextBytes(bufs_key);
                rad.NextBytes(bufs_iv);
                key = bufs_key;
                iv= bufs_iv;
            }

            var sha128 = new SHA3_SHAKE128();
            var sm3 = new SM3();
            var key_tag = sha128.Make(CombineBytes(key, bufs_pwd));
            var iv_tag = sha128.Make(Helper.ToBytes(sm3.Make(CombineBytes(iv, bufs_pwd))));
            return (key, iv, Helper.ToBytes(key_tag), Helper.ToBytes(iv_tag));
        }
    }
}
